#include "pch.h"
#include "EncAndDecDef.h"
#include "BitsConvert.h"

using namespace std;

const int Nb = 4;

const uint32_t tweakStart = 618033988;
const string tweakKey = "tweakKey_2024521";


Matrix<uint8_t> MIX_Matrix(4, 4, std::vector<uint8_t>{2, 3, 1, 1,
	1, 2, 3, 1,
	1, 1, 2, 3,
	3, 1, 1, 2});
Matrix<uint8_t> INV_MIX_Matrix(4, 4, std::vector<uint8_t>{0xE, 0xB, 0xD, 0x9,
	0x9, 0xE, 0xB, 0xD,
	0XD, 0x9, 0xE, 0xB,
	0xB, 0xD, 0x9, 0xE});
Matrix<uint8_t> S_BOX(16, 16, std::vector<uint8_t>{
	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
		0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
		0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
		0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
		0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
		0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
		0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
		0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
		0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
		0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
		0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
		0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
		0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
		0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
		0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
		0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
});
Matrix<uint8_t> INV_S_BOX(16, 16, std::vector<uint8_t>{
	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
		0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
		0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
		0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
		0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
		0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
		0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
		0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
		0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
		0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
		0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
		0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
		0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
		0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
		0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
		0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
});
std::map<uint32_t, uint32_t> RocnBuffer;

Matrix<uint8_t>* ConvertBitsToMatrix(const std::vector<uint8_t> bits) {
	if (bits.size() != 16) {
		throw "映射到矩阵的数组必须是一个16位长度的数组";
	}
	Matrix<uint8_t>* textMatrix = new Matrix<uint8_t>(4, 4);
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			(*textMatrix)[i][j] = bits[j * 4 + i];
		}
	}
	return textMatrix;

}
uint8_t Multiply2InGF256(const  uint8_t origin) {
	uint8_t result = 0;
	result = (origin << 1);
	if ((origin & 128) == 128) {
		result = result ^ 0x1b;
	}
	return result;
}
uint8_t Multiply(const uint8_t param1, const uint8_t pararm2) {
	std::map<uint8_t, uint8_t> bArrary;
	int mask = 1;
	for (int i = 0; i < 8; i++) {
		bArrary[i] = pararm2 & mask;
		mask = mask << 1;
	}
	int result = 0;
	for (int i = 0; i < 8; i++) {
		if (bArrary[i] == 0) {
			continue;
		}
		if (bArrary[i] == 1) {
			result = result ^ param1;
			continue;
		}
		int flag = bArrary[i];
		int temp = param1;
		while (flag != 1) {
			flag = flag >> 1;
			temp = Multiply2InGF256(temp);
		}
		result = result ^ temp;
	}
	return result;
}
uint32_t SubWord(uint32_t word) {
	uint32_t newword = 0;
	for (int i = 0; i < 4; i++) {
		uint8_t value = word & 255;
		word = word >> 8;
		newword = newword | S_BOX.get(value >> 4, value & 0xf);
		newword = newword >> 8;
	}
	return newword;
}
uint32_t RotWord(uint32_t word) {
	uint32_t temp = (word >> 24) & 0xff;
	word = (word << 8) | temp;
	return word;
}

uint32_t Rocn(const uint32_t& word) {
	if (RocnBuffer.find(word) != RocnBuffer.end())
	{
		return RocnBuffer[word];
	}
	uint32_t result = 1;
	for (uint32_t j = 0; j < word - 1; j++) {
		result = Multiply2InGF256(result);
	}
	int temp = result >> 8;
	RocnBuffer[word] = result << 24 | temp;
	return result << 24 | temp;
}
uint32_t Rocn(uint32_t&& word) {
	if (RocnBuffer.find(word) != RocnBuffer.end())
	{
		return RocnBuffer[word];
	}
	uint32_t result = 1;
	for (uint32_t j = 0; j + 1 < word; j++) {
		result = Multiply2InGF256(result);
	}
	int temp = result >> 8;
	RocnBuffer[word] = result << 24 | temp;
	return result << 24 | temp;
}
std::vector<uint32_t>* KeyExpansion(const ::std::vector<uint8_t> key) {
	const ::std::vector<uint8_t>* usedKeys = nullptr;
	int Nk = 4;
	int Nr = 10;
	if (key.size() == 16) {
		usedKeys = &key;
		Nk = 4;
		Nr = 10;
	}
	else if (key.size() != 16) {
		throw "Key length must be 16";
	}


	Matrix<uint8_t>* KeyMatrix = ConvertBitsToMatrix(*usedKeys);
	vector<uint32_t>* ExpandedKeys = new vector<uint32_t>();
	for (int i = 0; i < 4; i++) {
		uint32_t val = 0;
		for (int j = 0; j < 4; j++) {
			val = val | KeyMatrix->get(j, i);
			val = val << 8;
		}
		ExpandedKeys->push_back(val);
	}
	delete KeyMatrix;
	int i = Nk;
	while (i < Nb * (Nr + 1)) {
		int temp = ExpandedKeys->at(i - 1);
		if (i % Nk == 0) {
			temp = SubWord(RotWord(temp)) ^ Rocn(i / Nk - 1);
		}
		else if (Nk > 6 && i % Nk == 4) {
			temp = SubWord(temp);
		}

		uint32_t val = (*ExpandedKeys)[i - Nk] ^ temp;
		ExpandedKeys->push_back(val);
		i++;
	}
	return ExpandedKeys;
}
void SubBytes(Matrix<uint8_t>& state) {
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			state[i][j] = S_BOX.get(state[i][j] >> 4, state[i][j] & 0xf);
		}
	}
}
void InvSubBytes(Matrix<uint8_t>& state) {
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			state[i][j] = INV_S_BOX.get(state[i][j] >> 4, state[i][j] & 0xf);
		}
	}
}
void ShiftRows(Matrix<uint8_t>& state) {
	for (int i = 0; i < 4; i++) {
		uint8_t newrow[4] = { 0 };
		for (int j = 0; j < 4; j++) {
			newrow[j] = state.get(i, j);
		}
		int index = i;
		for (int j = 0; j < 4; j++) {
			state[i][j] = newrow[index];
			index = (index + 1) % 4;
		}
	}
}
void InvShiftRows(Matrix<uint8_t>& state) {
	for (int i = 0; i < 4; i++) {
		uint8_t newrow[4] = { 0 };
		for (int j = 0; j < 4; j++) {
			newrow[j] = state.get(i, j);
		}
		int index = (4 - i) % 4;
		for (int j = 0; j < 4; j++) {
			state[i][j] = newrow[index];
			index = (index + 1) % 4;
		}
	}
}
void MixColumns(Matrix<uint8_t>& state) {
	uint8_t newMatrix[4][4] = { 0 };

	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			for (int k = 0; k < 4; k++) {
				int value = Multiply(state[i][k], MIX_Matrix.get(k, j));
				value = value ^ newMatrix[i][j];
				newMatrix[i][j] = value;
			}
		}
	}
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			state[i][j] = newMatrix[i][j];
		}
	}
}
void InvMixColumns(Matrix<uint8_t>& state) {
	uint8_t newMatrix[4][4] = { 0 };

	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			for (int k = 0; k < 4; k++) {
				int value = Multiply(state[i][k], INV_MIX_Matrix.get(k, j));
				value = value ^ newMatrix[i][j];
				newMatrix[i][j] = value;
			}
		}
	}

	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			state[i][j] = newMatrix[i][j];
		}
	}
};
void AddRoundKey(Matrix<uint8_t>& state, const uint32_t* roundKey) {
	uint8_t  wstate[4] = { 0 };
	for (int i = 0; i < 4; i++) {
		uint32_t nowWkey = roundKey[i];
		for (int j = 0; j < 4; j++) {
			wstate[j] = (nowWkey >> (j * 8)) & 255;
		}
		for (int j = 0; j < 4; j++) {
			state[j][i] = state[j][i] ^ wstate[j];
		}
	}

};
std::vector<uint8_t>* Encrypt(const::std::vector<uint8_t> textCodes, const std::vector<uint8_t>& keyCodes) {
	Matrix<uint8_t>* textMatrix = ConvertBitsToMatrix(textCodes);
	std::vector<uint32_t>* expandedKeys = KeyExpansion(keyCodes);
	AddRoundKey(*textMatrix, expandedKeys->data());
	uint32_t* nowwarry = expandedKeys->data() + 4;
	for (int i = 1; i < 10; i++) {
		SubBytes(*textMatrix);
		ShiftRows(*textMatrix);
		MixColumns(*textMatrix);
		AddRoundKey(*textMatrix, nowwarry);
		nowwarry += 4;
	}
	SubBytes(*textMatrix);
	ShiftRows(*textMatrix);
	AddRoundKey(*textMatrix, nowwarry);
	std::vector<uint8_t>* result = new std::vector<uint8_t>(16);
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			(*result)[j * 4 + i] = textMatrix->get(i, j);
		}
	}
	delete textMatrix;
	delete expandedKeys;
	return result;
}
std::vector<uint8_t>* Decrypt(const::std::vector<uint8_t> textCodes, const std::vector<uint8_t>& keyCodes) {
	Matrix<uint8_t>* textMatrix = ConvertBitsToMatrix(textCodes);
	std::vector<uint32_t>* expandedKeys = KeyExpansion(keyCodes);
	AddRoundKey(*textMatrix, expandedKeys->data() + 40);
	uint32_t* nowwarry = expandedKeys->data() + 36;
	for (int i = 9; i > 0; i--) {
		InvSubBytes(*textMatrix);
		InvShiftRows(*textMatrix);
		AddRoundKey(*textMatrix, nowwarry);
		nowwarry -= 4;
		InvMixColumns(*textMatrix);
	}
	InvSubBytes(*textMatrix);
	InvShiftRows(*textMatrix);
	AddRoundKey(*textMatrix, nowwarry);
	std::vector<uint8_t>* result = new std::vector<uint8_t>(16);
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			(*result)[j * 4 + i] = textMatrix->get(i, j);
		}
	}
	delete textMatrix;
	delete expandedKeys;
	return result;
}
std::vector<uint8_t>* Encrypt(const::std::vector<uint8_t> textCodes, const std::vector<uint32_t>& expandedKey) {

	Matrix<uint8_t>* textMatrix = ConvertBitsToMatrix(textCodes);
	AddRoundKey(*textMatrix, expandedKey.data());
	const uint32_t* nowwarry = expandedKey.data() + 4;
	for (int i = 1; i < 10; i++) {
		SubBytes(*textMatrix);
		ShiftRows(*textMatrix);
		MixColumns(*textMatrix);
		AddRoundKey(*textMatrix, nowwarry);
		nowwarry += 4;
	}
	SubBytes(*textMatrix);
	ShiftRows(*textMatrix);
	AddRoundKey(*textMatrix, nowwarry);
	std::vector<uint8_t>* result = new std::vector<uint8_t>(16);
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			(*result)[j * 4 + i] = textMatrix->get(i, j);
		}
	}
	delete textMatrix;
	return result;
}
std::vector<uint8_t>* Decrypt(const::std::vector<uint8_t> textCodes, const std::vector<uint32_t>& expandedKey) {
	Matrix<uint8_t>* textMatrix = ConvertBitsToMatrix(textCodes);
	AddRoundKey(*textMatrix, expandedKey.data() + 40);
	const uint32_t* nowwarry = expandedKey.data() + 36;
	for (int i = 9; i > 0; i--) {
		InvSubBytes(*textMatrix);
		InvShiftRows(*textMatrix);
		AddRoundKey(*textMatrix, nowwarry);
		nowwarry -= 4;
		InvMixColumns(*textMatrix);
	}
	InvSubBytes(*textMatrix);
	InvShiftRows(*textMatrix);
	AddRoundKey(*textMatrix, nowwarry);
	std::vector<uint8_t>* result = new std::vector<uint8_t>(16);
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			(*result)[j * 4 + i] = textMatrix->get(i, j);
		}
	}
	delete textMatrix;
	return result;
}

std::vector<uint8_t>* gettweak(uint32_t tweak, const string key) {
	std::vector<uint8_t> tweakCodes(16, 0);

	for (size_t i = 0; i < 4; i++)
	{
		uint32_t temp = tweak + 1;
		for (size_t j = 0; j < 4; j++)
		{

			tweakCodes[i * 4 + j] = (temp) & 255;
			temp = temp >> 8;
		}
	}

	return Encrypt(tweakCodes, std::vector<uint8_t>(key.cbegin(), key.cend()));
}

std::vector<uint8_t>* DecryptText(const std::string decrypedText, const std::string key) {
std:vector<uint8_t>* textCodes = RefCharsToU8Arr(decrypedText);
	const std::vector<uint8_t> keyCodes(key.cbegin(), key.cend());
	std::vector<uint8_t>* tempKeys = nullptr;
	std::vector<uint8_t>* resultCodes = new std::vector<uint8_t>(textCodes->size(), 0);
	for (int i = 0; i < textCodes->size() / 16; i++) {
		if ((i + 1) * 16 > textCodes->size()) {
			throw "传入的密文错误\n";
		}
		if (tempKeys != nullptr)
		{
			throw "未正确的释放临时内存";
		}
		tempKeys = Decrypt(std::vector<uint8_t>(textCodes->begin() + i * 16, textCodes->begin() + (i + 1) * 16), keyCodes);
		auto tweak = gettweak(tweakStart + i, tweakKey);
		for (int j = 0; j < 16; j++) {
			(*resultCodes)[i * 16 + j] = (*tempKeys)[j] ^ (*tweak)[j];
		}

		delete tweak;
		delete tempKeys;
		tempKeys = nullptr;
	}
	delete textCodes;
	return resultCodes;
}

std::vector<uint8_t>* ConvertTextToCodes(const std::string plainText) {
	std::vector<uint8_t>* resultCodes = new std::vector<uint8_t>(plainText.begin(), plainText.end());
	if (resultCodes->size() % 16 != 0) {
		resultCodes->resize(resultCodes->size() + (16 - (resultCodes->size() % 16)), 0);
	}
	return resultCodes;
}
std::stringbuf* EncryptText(const std::string plainText, const std::string key) {
	std::vector<uint8_t>* textCodes = ConvertTextToCodes(plainText);
	std::vector<uint8_t> keyCodes(key.cbegin(), key.cend());
	std::vector<uint8_t> result;
	std::vector<uint8_t>* tempKeys = nullptr;
	for (int i = 0; i < textCodes->size() / 16; i++) {
		if ((i + 1) * 16 > textCodes->size()) {
			throw "传入的明文错误\n";
		}
		if (tempKeys != nullptr)
		{
			throw "未正确的释放临时内存";
		}
		auto tweak = gettweak(tweakStart + i, tweakKey);

		auto plainText = std::vector<uint8_t>(textCodes->begin() + i * 16, textCodes->begin() + (i + 1) * 16);

		for (int j = 0; j < plainText.size(); j++) {
			plainText[j] = plainText[j] ^ tweak->at(j);
		}
		tempKeys = Encrypt(plainText, keyCodes);
		result.insert(result.end(), tempKeys->cbegin(), tempKeys->cend());
		delete tweak;
		delete tempKeys;
		tempKeys = nullptr;
	}

	delete textCodes;;
	return RefToChars(result);
}
